// T
// 没使用泛型：在定义之初就需要指定具体类型
type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;

// 普通函数使用泛型
function sayHelloWord<T>(a: T, b: T): string {
    return `${a} 对 ${b} 说：你好`;
}
const o3 = sayHelloWord(1, 2);
const o4 = sayHelloWord<string>("张三", "李四");
console.log(o3);
console.log(o4);

// 箭头函数使用泛型
const sayHelloW = <T>(a: T, b: T): string => {
    return `${a} 对 ${b} 说：你好`;
};
const o1 = sayHelloW("张三W", "李四W");
const o2 = sayHelloW<number>(100, 200);
console.log(o1);
console.log(o2);

// 多个泛型参数
const output = <T, V>(e1: T, e2: T, name: V, unit: V): string =>
    `${name}: ${e1} + ${e2} ${unit}`;
const o5 = output(1, 9, "sum", "个");
console.log(o5)

// 对象的泛型
const arr2: Array<number> = [1]

// 后续自定义
// interface ObjPerson<Info> {
//     info: Info,
//     getInfo: () => Info
// }

// 给默认值
interface ObjPerson<Info = string | number | symbol> {
    info: Info,
    getInfo: () => Info
}

const redPerson: ObjPerson<string> = {
    info: 'red',
    getInfo() {
        return 'red'
    },
}

const greenPerson: ObjPerson<number> = {
    info: 4,
    getInfo() {
        return 6
    }
}

// 类的泛型
class NicePerson<Info> {
    info: Info
    constructor(info: Info) {
        this.info = info
    }
    getInfo() {
        return this.info
    }
}

const pPerson = new NicePerson<string>('nice')
pPerson.getInfo()

// const tPerson = new NicePerson<string>(6) // Argument of type 'number' is not assignable to parameter of type 'string'.
const tPerson = new NicePerson<number>(6)
tPerson.getInfo()


// 泛型约束
// extends：继承、泛型类型的约束、条件类型的约束
// 1、继承
class BasePerson<Info> {
    info: Info
    constructor(info: Info) {
        this.info = info
    }
    getInfo() {
        return this.info
    }
}

class EPerson<Info> extends BasePerson<Info> {
    sayHello() {
        return `hello, ${this.info}`
    }
}

const ePerson = new EPerson(100)
console.log(ePerson.sayHello())


// 2、泛型类型的约束：默认值的基础上还要限制类型
type MPersonInfo = number | string | boolean
class MPerson<Info extends MPersonInfo = boolean> { // 默认值必须为 MPersonInfo 中的类型之一
    info: Info
    constructor(info: Info) {
        this.info = info
    }
    sayHello() {
        return `hello, ${this.info}`
    }
}

const mPerson = new MPerson(100)
console.log(mPerson.sayHello())

// class MPerson<number | string | boolean> { // × 错误：不支持该写法
//    ... // 伪代码
// }

// 3、条件类型的约束
// 示例一
type Gender = '男' | '女'
const gender: Gender = '男'
// 函数写法
const isMan = (gender: | Gender | undefined = '男') => gender === '男' ? true : false // √：正确写法 gender: | Gender | undefined = '男'
// const isMan = (gender?: | Gender | undefined = '男') => gender === '男' ? true : false // ×：gender?: | Gender | undefined = '男' 。? 和 默认值之前存在冲突，所以使用上面的写法

const res = isMan(gender)
console.log(res)

// 示例二
// 类的写法
type IsMan<G> = G extends '男' ? true : false
const isMan2: IsMan<'男'> = true
const isMan3: IsMan<'女'> = false
let m: IsMan<'男'> // m 类型推导为 true
m = true
// m = false // ×：不能将类型“false”分配给类型“true”。
console.log(isMan2)
console.log(isMan3)
console.log(m)

// 泛型工具类型
// 1、Partial<T>：将某个类型的属性变为可选
interface Broad {
    name: string,
    length: number,
}
let broad: Partial<Broad> = {} // 对所有参数可选
broad.name = '长江大桥'
// broad.b = 'qqw' // ×：类型“Broad”上不存在属性“b”。

// 2、Readonly<T>：将某个类型的属性变为只读
let build: Readonly<Broad> = {
    name: '长江大桥',
    length: 1000,
}
// build.name = '长江大桥2' // ×：无法分配到 "name" ，因为它是只读属性。

// 3、Record<K, T>：构造一个对象类型，其属性键为K，键值为T
type BroadRecord = Record<string | number, Broad>
let broadRecord: BroadRecord = {
    '长江大桥': {
        name: '长江大桥',
        length: 1000,
    },
    100: {
        name: '长江大桥',
        length: 1000,
    },
}